#pragma once

/*
 * kcp connection.
 */

#include "knet.h"
#include "kcp_typedef.h"
#include "wrapper_object_pool.h"
#include "asio/asio.hpp"

namespace KCP {
	class CConnectionMgr;
	using namespace asio::ip;

	class CConnection : public IKConnection {
	public:
		explicit CConnection(CConnectionMgr& manager);
		virtual ~CConnection();
		DYNAMIC_SIMPLE_DECLARE(CConnection, 16, 8);
		// connection state.
		enum eConnectionState {
			// invalid state
			conn_state_invalid = -1,

			// connecting state
			conn_state_connecting,

			// connected state
			conn_state_connected,
		};

	public:
		// Create connection.
		static CConnection* create(CConnectionMgr& manager, uint32 conv, const udp::endpoint& endpoint);
		// Release connection.
		static void release(CConnection* conn);

	public:
		// implement IKConnection interface.
		// get conv.
		virtual unsigned int GNAPI GetConv() { return m_conv; }

		// disconnect.
		virtual void         GNAPI Disconnect();

		// send message.
		virtual void         GNAPI SendMsg(const char* msg, int len);

	public:
		// update kcp.
		void       update(int64 clockTime);

		// endpoint.
		void       set_endpoint(const udp::endpoint& endpoint);
		const udp::endpoint& get_endpoint() const;

		// input data.
		void       input(const char* data, size_t len, const udp::endpoint& endpoint);

		// time check.
		bool       isTimeout() const;

		// close connection.
		void       close();

		// user session interface.
		void       setSession(IKSession* s);
		IKSession* getSession() { return m_session; }

		// state
		void       setState(eConnectionState state);
		eConnectionState getState();

		void       setConnectingTime(int64 time);
		int64      getConnectingTime() const;

		// reSend establish pack to peer.
		void       reSendEstablishPack();

	private:
		void       init_kcp(uint32 conv);
		void       clean();

		// send udp message.
		void       send_udp_pack(const char* buf, int len);

		// data output callback.
		static int udp_output(const char* buf, int len, ikcpcb* kcp, void* user);

	private:
		CConnectionMgr& m_connectionMgr;
		uint32           m_conv{ 0 };
		ikcpcb*          m_kcp{nullptr};
		udp::endpoint    m_endpoint;
		int64            m_lastRecvTime{ 0 };
		IKSession*       m_session{nullptr};
		eConnectionState m_state{ conn_state_invalid };
		int64            m_connecting_time{ 0 };
	};
}